package cn.bitkit.tools.common;

import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;

import cn.bitkit.base.util.DateUtil;
import cn.bitkit.base.util.StringUtil;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author changbo
 */
public class DateCnUtil {

	private static final DateTimeFormatter DF_DATE_CN = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
	private static final DateTimeFormatter DF_DATE_TIME_CN = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
	public static final String[] WEEK_DAY_CN = new String[]{"星期一","星期二","星期三","星期四","星期五","星期六","星期天"};
    /**
     * //1900-2100
     * 参考<a href="https://github.com/jjonline/calendar.js/blob/master/calendar.js">...</a>
     *
     */
	public static final long[] LUNAR_INFO = new long[] {
    		0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
            0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
            0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
            0x06566, 0x0d4a0, 0x0ea50, 0x16a95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
            0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
            0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0,
            0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
            0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6,
            0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
            0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0,
            0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
            0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
            0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
            0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
            0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,
            0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0,
            0x092e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4,
            0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0,
            0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160,
            0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252,
            0x0d520};
    public static final int MIN_YEAR = 1900;
    public static final int MAX_YEAR = 2100;
	public static final int MONTH = 12;
    public static final String LUNAR_MIN_DAY = "1900-01-01";
    public static final String SOLAR_MIN_DAY = "1900-01-31";
    
    public static String toDateCn(Date date){
    	return DateUtil.format(date, DF_DATE_CN);
    }
    
    public static String toDateTimeCn(Date date){
    	return DateUtil.format(date, DF_DATE_TIME_CN);
    }
    
    public static String toWeekDayCn(Date date){
    	Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	int wd = cal.get(Calendar.DAY_OF_WEEK) -1;
    	wd = (wd == 0) ? 7 : wd;
    	return WEEK_DAY_CN[wd-1];
    }
    
	public static Date lunarToSolar(String lunar) {
		return lunarToSolar(new LunarDate(lunar, false));
	}

	public static Date lunarToSolar(String lunar, boolean leapMonth) {
		return lunarToSolar(new LunarDate(lunar, leapMonth));
	}

	/**
	 * 1.阴历日期lunarMinDay到该日期相差的天数
	 * 2.阳历日期solarMinDay加上天数
	 */
	public static Date lunarToSolar(LunarDate lunarDate) {
		int offset = 0;
		//历史年份累计
		for(int y = MIN_YEAR; y<lunarDate.getYear(); y++) {
			for(int m = 1; m <= MONTH; m++) {
				offset += getMonthDays(y, m);
				//加上闰月天数
				offset += getLeapMonthDays(y, m);
			}
		}
		//本年累计
		for(int m = 1; m < lunarDate.getMonth(); m++) {
			offset += getMonthDays(lunarDate.getYear(), m);
			//加上闰月天数
			offset += getLeapMonthDays(lunarDate.getYear(), m);
		}
		//本月累计
		//如果是闰月
		if (lunarDate.getLeapMonth() && getLeapMonthDays(lunarDate.getYear(), lunarDate.getMonth()) > 0) {
			offset += getMonthDays(lunarDate.getYear(), lunarDate.getMonth());
			offset += (lunarDate.getDay()-1);
		}else {
			offset += (lunarDate.getDay()-1);
		}
        Date baseDay = DateUtil.parseDate(SOLAR_MIN_DAY);
		return DateUtil.addDays(baseDay, offset);
	}
	
	private static int getMonthDays(int year, int month) {
		long lunarYear = LUNAR_INFO[year - MIN_YEAR];
		return (lunarYear & (0x10000 >> month)) != 0 ? 30 : 29;
	}
	
	private static int getLeapMonthDays(int year, int month) {
		long lunarYear = LUNAR_INFO[year - MIN_YEAR];
		//如果是闰月
		if ((lunarYear & 0xf) == month) {
			//返回闰月天数
			return (lunarYear & 0x10000) != 0 ? 30 : 29;
        }
		return 0;
	}
	
	public static boolean isLeapMonth(Date date) {
		LunarDate lunarDate = solarToLunarDate(date);
		return lunarDate.getLeapMonth();
	}
	
	public static String solarToLunar(Date date) {
		LunarDate lunarDate = solarToLunarDate(date);
		return lunarDate.getDate();
	}
	
	public static LunarDate solarToLunarDate(Date date) {
        Date baseDay = DateUtil.parseDate(SOLAR_MIN_DAY);
		int offset = DateUtil.getDays(date, baseDay);
		int sum = 0;
		for(int y = MIN_YEAR; y <= MAX_YEAR; y++) {
			for(int m = 1; m <= MONTH; m++) {
				int d = getMonthDays(y, m);
				if(sum+d >= offset) {
					return new LunarDate(y, m, offset - sum, false);
				}else {
					sum += d;
				}
				d = getLeapMonthDays(y, m);
				//如果是闰月
				if(d > 0) {
					if(sum+d >= offset) {
						return new LunarDate(y, m, offset - sum, true);
					}else {
						sum += d;
					}
				}
			}
		}
		throw new RuntimeException("solarToLunarDate error");
	}

	@Data
	@NoArgsConstructor
	public static class LunarDate {
		private String date;
		private int year;
		private int month;
		private int day;
		private Boolean leapMonth;
		public LunarDate(String date, boolean leapMonth) {
			super();
			this.date = date;
			this.leapMonth = leapMonth;
			this.year = StringUtil.toInt(date.substring(0, 4));
			this.month = StringUtil.toInt(date.substring(5, 7));
			this.day = StringUtil.toInt(date.substring(8, 10));
		}
		public LunarDate(int year, int month, int day, Boolean leapMonth) {
			super();
			this.year = year;
			this.month = month;
			this.day = day;
			this.leapMonth = leapMonth;
			String yStr = StringUtil.leftPad(""+year, '0', 2);
			String mStr = StringUtil.leftPad(""+month, '0', 2);
			String dStr = StringUtil.leftPad(""+(day+1), '0', 2);
			this.date = yStr+"-"+mStr+"-"+dStr;
		}

	}

}
